using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace ConeFabric.Troy.Core
{
    public class Model
    {
        public Assembly Load(string assemblyFile)
        {
            assembly = Assembly.LoadFrom(assemblyFile);
            return assembly;
        }

        public string[] GetMethods()
        {
            return GetMethods(null);
        }

        public string[] GetMethods(string attributeType)
        {
            if (methods == null)
                LoadMethods(attributeType);
            return methods.ToArray();
        }


        public static void WriteMethods(string[] methods, System.IO.StringWriter writer)
        {
            int counter = 1;
            foreach (string method in methods)
            {
                writer.WriteLine("  {0}. {1}", counter++, Parse(method));
            }
        }

        public static void WriteExceptions(Exception[] exceptions, System.IO.StringWriter writer)
        {
            int counter = 1;
            writer.WriteLine("{0} Exceptions were found.", exceptions.Length);
            foreach (Exception exception in exceptions)
            {
                writer.WriteLine("  {0}. {1}", counter++, exception);
            }
        }

        public Exception[] LoaderExceptions
        {
            get 
            {
                if (loaderExceptions == null)
                    loaderExceptions = new Exception[] { };
                return loaderExceptions; 
            }
        }

        public Dictionary<string, string[]> GetTypeHierarchy(string attributeType)
        {
            Dictionary<string, string[]> hierarhcy = new Dictionary<string, string[]>();
            LoadTypes();

            foreach (Type type in types)
            {
                if (type != null)
                {
                    List<string> typeMethods = new List<string>();
                    foreach (MemberInfo info in type.GetMembers())
                    {
                        if (attributeType != null)
                        {
                            object[] attributes = info.GetCustomAttributes(true);
                            if (attributes.Length == 0)
                                continue;
                            if (!AttributeExistsIn(attributeType, attributes))
                                continue;
                        }
                        typeMethods.Add(info.Name);
                    }
                    if(typeMethods.Count > 0)
                        hierarhcy[type.Name] = typeMethods.ToArray();
                }
            }

            return hierarhcy;
        }

        public static void WriteHierarchy(Dictionary<string, string[]> hierarchy, System.IO.StringWriter writer)
        {
            foreach (string type in hierarchy.Keys)
            {
                writer.WriteLine("[{0}]", type);
                WriteMethods(hierarchy[type], writer);
                writer.WriteLine();
            }
        }

        private static string Parse(string method)
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(method[0]);

            foreach (char ch in method.Substring(1,method.Length-1))
            { 
                if(Char.IsUpper(ch))
                    builder.Append(string.Format(" {0}", Char.ToLower(ch)));
                else
                    builder.Append(ch);
            }
            return builder.ToString();
        }

        private void LoadMethods(string attributeType)
        {
            LoadTypes();
            methods = new List<string>();
            if (types == null)
                return;
            foreach (Type type in types)
            {
                if (type != null)
                {
                    foreach (MemberInfo info in type.GetMembers())
                    {
                        if (attributeType != null)
                        {
                            object[] attributes = info.GetCustomAttributes(true);
                            if (attributes.Length == 0)
                                continue;
                            if (!AttributeExistsIn(attributeType, attributes))
                                continue;
                        }
                        methods.Add(info.Name);
                    }
                }
            }
        }

        private bool AttributeExistsIn(string attributeType, object[] attributes)
        {
            foreach (object attribute in attributes)
                if (attribute.GetType().FullName == attributeType)
                    return true;
            return false;
        }
        private void LoadMethods()
        {
            LoadMethods(null);
        }

        private void LoadTypes()
        {
            try
            {
                if (types == null)
                    types = assembly.GetTypes();
            }
            catch(ReflectionTypeLoadException loadException)
            {
                loaderExceptions = loadException.LoaderExceptions;
            }
        }

        private Exception[] loaderExceptions;
        private Assembly assembly;
        private Type[] types;
        private List<string> methods;
    }
}
